package service

import (
	"context"
	"errors"
	"fmt"
	"github.com/duke-git/lancet/v2/cryptor"
	"github.com/duke-git/lancet/v2/random"
	"github.com/duke-git/lancet/v2/slice"
	"gorm.io/gorm"
	"server/component"
	"server/repository/dao"
	"server/repository/model"
	"strings"
)

var Us = UserService{UserDao: &dao.UserDao{dao.Repo[model.UserModel]{component.Db}}}

type UserService struct {
	UserDao *dao.UserDao
}

type UserRegisterOpt struct {
	Name string
	Pwd  string
}

func (o *UserService) Register(ctx context.Context, opt *UserRegisterOpt) (*model.UserModel, error) {
	if o.UserDao.IsNameExist(ctx, opt.Name) {
		return nil, ErrUserExist
	}
	userModel := &model.UserModel{
		Name:  opt.Name,
		Pwd:   o.buildPwd(opt.Pwd),
		Uuid:  o.buildUuid(),
		Token: o.buildToken(),
	}

	if err := o.UserDao.Create(ctx, userModel); err != nil {
		return nil, err
	}
	return userModel, nil
}

func (o *UserService) Login(ctx context.Context, name string, pwd string) (*model.UserModel, error) {
	userModel, err := o.UserDao.FindByName(ctx, name)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrUserNotExist
		}
		return nil, err
	}
	checkPwd := o.buildPwd(pwd)
	if userModel.Pwd != checkPwd {
		return nil, ErrPassword
	}
	userModel.Token = o.buildToken()
	err = component.Db.Save(userModel).Error
	if err != nil {
		return nil, err
	}
	return userModel, nil
}

type ChangePwdOpt struct {
	User   *model.UserModel
	OldPwd string
	NewPwd string
}

func (o *UserService) ChangePwd(ctx context.Context, opt *ChangePwdOpt) error {
	checkPwd := o.buildPwd(opt.OldPwd)
	if opt.User.Pwd != checkPwd {
		return ErrPassword
	}
	opt.User.Pwd = o.buildPwd(opt.NewPwd)
	return component.Db.WithContext(ctx).Save(opt.User).Error
}

const (
	OnlineStatusOnline  = 1
	OnlineStatusOffLine = 2
)

type ChangeOnlineOpt struct {
	UserUni string
	Event   int
	Gateway string
}

func (o *UserService) ChangeOnline(ctx context.Context, opt *ChangeOnlineOpt) error {
	user, err := o.UserDao.FindByUuid(ctx, opt.UserUni)
	if err != nil {
		return err
	}

	var gateways []string
	if len(user.Gateway) > 0 {
		gateways = strings.Split(user.Gateway, ",")
	}

	if opt.Event == OnlineStatusOnline {
		gateways = append(gateways, opt.Gateway)
		gateways = slice.Unique(gateways)
		if user.OnLine == 0 {
			user.OnLine = 1
		}
	}

	if opt.Event == OnlineStatusOffLine {
		gateways = component.SliceDelete(gateways, opt.Gateway)
		if len(gateways) == 0 {
			user.OnLine = 0
		}
	}

	fmt.Println("gateways", gateways)
	nStr := strings.Join(gateways, ",")
	fmt.Println("nStr", nStr)
	user.Gateway = nStr

	return component.Db.WithContext(ctx).Save(user).Error
}

func (o *UserService) buildPwd(pwd string) string {
	return cryptor.Md5String(pwd)
}

func (o *UserService) buildToken() string {
	return random.RandNumeralOrLetter(32)
}

func (o *UserService) buildUuid() string {
	return random.RandNumeralOrLetter(16)
}

func (o *UserService) InitOnline(ctx context.Context) error {
	tx := o.UserDao.Model(ctx).
		Session(&gorm.Session{AllowGlobalUpdate: true}).
		Updates(map[string]any{
			"on_line": 0,
			"gateway": "",
		})
	return tx.Error
}

type ImLoginOpt struct {
	Token   string
	Ip      string
	Gateway string
}

func (o *UserService) ImLogin(ctx context.Context, opt *ImLoginOpt) (*model.UserModel, error) {
	userModel, err := o.UserDao.FindByToken(ctx, opt.Token)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrUserNotExist
		}
		return nil, err
	}

	return userModel, nil
}
